// VincentView.cpp : implementation of the CVincentView class
//

#include "stdafx.h"
#include "Vincent.h"

#include "VincentDoc.h"
#include "VincentView.h"

#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CVincentView

IMPLEMENT_DYNCREATE(CVincentView, CScrollView)

BEGIN_MESSAGE_MAP(CVincentView, CScrollView)
	//{{AFX_MSG_MAP(CVincentView)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_RBUTTONDOWN()
	ON_WM_SETCURSOR()
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CVincentView construction/destruction

CVincentView::CVincentView()
{
	m_pCurrentNode = NULL;
}

CVincentView::~CVincentView()
{
}

BOOL CVincentView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CScrollView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CVincentView drawing

void CVincentView::OnDraw(CDC* pDC)
{
	CVincentDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	
	// 이미지 사이즈의 나머지 부분을 진회색으로 그려넣는 부분
	CRect rt;
	GetClientRect(&rt);				// 클라이언트 영역의 사이즈를 얻어온다.
	
	// 현재 클라이언트 영역이 이미지 사이즈보다 작으면, 칠할 범위를 늘린다.
	rt.right = max(rt.right, pDoc->GetImageSize().cx);
	rt.bottom = max(rt.bottom, pDoc->GetImageSize().cy);
	
	// 클라이언트 영역 전체를 색칠한다. (회색으로 된 배경임)
	pDC->FillSolidRect(&rt, RGB(128, 128, 128));
	
	// 도큐먼트의 히스토리에 저장된 캐쉬를 그린다.
	// 노드가 추가될때마다 캐쉬는 업데이트 된다.
	pDoc->GetWorkHistory()->Draw(pDC);
}

/////////////////////////////////////////////////////////////////////////////
// CVincentView printing

BOOL CVincentView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}

void CVincentView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add extra initialization before printing
}

void CVincentView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add cleanup after printing
}

/////////////////////////////////////////////////////////////////////////////
// CVincentView diagnostics

#ifdef _DEBUG
void CVincentView::AssertValid() const
{
	CScrollView::AssertValid();
}

void CVincentView::Dump(CDumpContext& dc) const
{
	CScrollView::Dump(dc);
}

CVincentDoc* CVincentView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CVincentDoc)));
	return (CVincentDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CVincentView message handlers

void CVincentView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();
	SetScrollSizes(MM_TEXT, GetDocument()->GetImageSize());
}

void CVincentView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	CScrollView::OnUpdate(pSender, lHint, pHint);
	
	// CDocument에서 UpdateAllViews()를 호출할때 갱신해준다.
	SetScrollSizes(MM_TEXT, GetDocument()->GetImageSize());
}

void CVincentView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// 마우스를 캡쳐한다.
	SetCapture();
	
	// 메시지 박스 등에 의해서 OnLButtonUp()이 호출되지 않을 수도 있다!!
	if (m_pCurrentNode != NULL)	// 그럴 경우에는,
		delete m_pCurrentNode;	// 메모리를 해제하고,
	
	// 객체를 생성한다. (new로 메모리 할당이 됨.)
	m_pCurrentNode = CreateNode(GetDocument()->GetToolsType());
	
	// 현재 View의 DC를 얻어온다.
	CClientDC dc(this);
	
	// 이미지 사이즈를 넘는 곳은 안그려지게끔 클리핑을 한다.
	CRgn rgn;
	rgn.CreateRectRgn(0, 0, GetDocument()->GetImageSize().cx, GetDocument()->GetImageSize().cy);
	dc.SelectClipRgn(&rgn, RGN_COPY);
	
	// 작업하는 동안 모래시계로 바꾼다.
	// 색칠 작업 말고는 순식간이기 때문에, 바뀐지도 모른다.
	// 색칠 작업은 오래 걸리기 때문에, 모래시계가 보인다.
	// 모래시계동안은 클릭이 안됨.
	BeginWaitCursor();
	
	// 할당이 성공했으면, OnLButtonDown()을 호출한다.
	if (m_pCurrentNode)
	{
		m_pCurrentNode->OnLButtonDown(&dc, nFlags, point, CPoint(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT)));
		
		// FillUp 등은 Down에서 모든 처리가 끝난다.
		if (m_pCurrentNode->IsComplete())	// 완료된 노드이면,
		{
			GetDocument()->GetWorkHistory()->AddNode(m_pCurrentNode);	// 작업히스토리에 추가한다.
			m_pCurrentNode = NULL;	// 작업하던 노드는 NULL로 셋팅.
			ReleaseCapture();		// 마우스 캡쳐를 해제한다.
		}
	}
	else
	{
		ReleaseCapture();	// 할당되지 않았으면, 마우스 캡쳐를 해제한다.
	}
	
	// 작업하는 동안 모래시계로 바꾼다.
	EndWaitCursor();
	
	CScrollView::OnLButtonDown(nFlags, point);
}

void CVincentView::OnRButtonDown(UINT nFlags, CPoint point) 
{
	// 현재는 OnRbuttonDown()을 지원하는 것은 CGetColor밖에 없다.
	
	// 객체를 생성한다. (new로 메모리 할당이 됨.)
	CDrawingNode* pNode = CreateNode(GetDocument()->GetToolsType());
	
	// 현재 View의 DC를 얻어온다.
	CClientDC dc(this);
	
	// 이미지 사이즈를 넘는 곳은 안그려지게끔 클리핑을 한다.
	CRgn rgn;
	rgn.CreateRectRgn(0, 0, GetDocument()->GetImageSize().cx, GetDocument()->GetImageSize().cy);
	dc.SelectClipRgn(&rgn, RGN_COPY);
	
	// 할당이 성공했으면, OnLButtonDown()을 호출한다.
	if (pNode)
		pNode->OnRButtonDown(&dc, nFlags, point, CPoint(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT)));
	
	CScrollView::OnRButtonDown(nFlags, point);
}

void CVincentView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CClientDC dc(this);
	
	// 마우스가 캡쳐된 상태고, 객체가 있으면..
	if (GetCapture() == this && m_pCurrentNode != NULL)
	{
		// 이미지 사이즈를 넘는 곳은 안그려지게끔 클리핑을 한다.
		CRgn rgn;
		rgn.CreateRectRgn(0, 0, GetDocument()->GetImageSize().cx, GetDocument()->GetImageSize().cy);
		dc.SelectClipRgn(&rgn, RGN_COPY);
		
		// 다형성으로 OnLButtonUp()을 호출한다.
		m_pCurrentNode->OnLButtonUp(&dc, nFlags, point, CPoint(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT)));
		
		// 완료된 노드이면, 작업히스토리에 추가한다.
		if (m_pCurrentNode->IsComplete())
			GetDocument()->GetWorkHistory()->AddNode(m_pCurrentNode);
		// 완료된 것이 아니면 이 노드는 삭제
		else
			delete m_pCurrentNode;
		
		// 작업하던 노드는 NULL로 셋팅.
		m_pCurrentNode = NULL;
		
		// 마우스 캡쳐를 해제한다.
		ReleaseCapture();
	}
	
	CScrollView::OnLButtonUp(nFlags, point);
}

void CVincentView::OnMouseMove(UINT nFlags, CPoint point) 
{
	CClientDC dc(this);
	
	// 상태바에 마우스의 논리적 좌표를 찍어준다.
	CRect rt;
	GetClientRect(&rt);
	CString coord;
	coord.Format("(%d,%d)", point.x + GetScrollPos(SB_HORZ), point.y + GetScrollPos(SB_VERT));
	((CMainFrame*)AfxGetMainWnd())->GetStatusBar()->SetPaneText(1, coord);
	
	// 마우스가 캡쳐된 상태고, 객체가 있으면..
	if (GetCapture() == this && m_pCurrentNode != NULL)
	{
		// 이미지 사이즈를 넘는 곳은 안그려지게끔 클리핑을 한다.
		CRgn rgn;
		rgn.CreateRectRgn(0, 0, GetDocument()->GetImageSize().cx, GetDocument()->GetImageSize().cy);
		dc.SelectClipRgn(&rgn, RGN_COPY);
		
		// OnMouseMove()를 호출한다. 실질적인 작업.
		m_pCurrentNode->OnMouseMove(&dc, nFlags, point, CPoint(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT)));
	}
	
	CScrollView::OnMouseMove(nFlags, point);
}

CDrawingNode* CVincentView::CreateNode(UINT nID)
{
	CDrawingNode* pNode;
	
	// ID에 해당하는 객체를 생성해서 리턴해준다.
	switch (nID) {
	case ID_TOOLS_FREEDRAW:
		pNode = new CFreeDraw();
		break;
	case ID_TOOLS_DRAWRECT:
		pNode = new CDrawRect();
		break;
	case ID_TOOLS_DRAWCIRCLE:
		pNode = new CDrawCircle();
		break;
	case ID_TOOLS_DRAWLINE:
		pNode = new CDrawLine();
		break;
	case ID_TOOLS_ERASER:
		pNode = new CEraser();
		break;
	case ID_TOOLS_FILLUP:
		pNode = new CFillUp();
		break;
	case ID_TOOLS_GETCOLOR:
		pNode = new CGetColor();
		break;
	default:
		AfxMessageBox("잘못된 버튼 아이디입니다.");
		pNode = NULL;
	}
	
	return pNode;
}

BOOL CVincentView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	// 마우스가 뷰에 있을때, 마우스 커서를 펜모양으로 설정한다.
	SetCursor(AfxGetApp()->LoadCursor(IDC_CURSOR_PEN));
	
	// 베이스 클래스의 OnSetCursor를 호출하지 말고, 반드시 리턴 TRUE로 해야한다.
	// 그래야 다시 안바뀜.
	return TRUE;
}

